package com.lhl.netty.client;

import com.alibaba.fastjson.JSON;
import com.lhl.netty.client.listener.ServerMsgEvent;
import com.lhl.netty.client.listener.ServerMsgListener;
import com.lhl.netty.client.ssl.SslContextFactory;
import com.lhl.netty.common.*;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

public abstract class NetClient {
    private static final Logger logger = LoggerFactory.getLogger(NetClient.class);

    private String serverName;
    private String serverIP;
    private int serverPort;

    private Bootstrap b = null;
    private EventLoopGroup workerGroup = null;
    private ChannelFuture f = null;

    protected abstract void connecting();
    protected abstract void connected(String msg);
    protected abstract void disConnected();
    protected abstract void reciveMsg(String msg);

    private ServerMsgListener serverMsgListener;

    public NetClient(String serverName, String serverIP, int serverPort){
        this.serverName = serverName;
        this.serverIP =serverIP;
        this.serverPort = serverPort;

        new Thread(netClientRunnable).start();
        //向p1注册一个监听器
        this.register(new ServerMsgListener() {
            @Override
            public void onConnecting() {
                connecting();
            }

            @Override
            public void onConnected(ServerMsgEvent e) {
                connected(e.getMsg());
            }

            @Override
            public void onDisConnected() {
                disConnected();
            }

            @Override
            public void onReciveMsg(ServerMsgEvent e) {
                reciveMsg(e.getMsg());
            }
        });
    }

    /**
     * 发送消息到服务端
     * @param token token
     * @param type type
     * @param content 内容
     */
    public void sendMsg(String token, int type, String content){
        sendMsg(new NetMessage(NetMessageType.MESSAGE_USER_INPUT,
                JSON.toJSONString(new AppMsg(token,type,content))));
    }

    /**
     * 断开与服务器的连接
     */
    public void disConnect(){
        disConnectNet();
    }


    //监听到连接服务器失败时，会在3秒后重新连接
    private ChannelFutureListener channelFutureListener = new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture f) throws Exception {
            if (f.isSuccess()) {
                logger.debug("连接"+serverName+"成功");
            } else {
                logger.debug("连接"+serverName+"失败");
                //  3秒后重新连接
                f.channel().eventLoop().schedule(new Runnable() {
                    @Override
                    public void run() {
                        connect();
                    }
                }, 3, TimeUnit.SECONDS);
            }
        }
    };

    private void init() throws Exception {
        final SSLContext sslCtx = SslContextFactory.getClientContext();

        workerGroup = new NioEventLoopGroup();
        try {
            b = new Bootstrap(); // (1)
            b.group(workerGroup); // (2)
            b.channel(NioSocketChannel.class); // (3)
            b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {

                    SSLEngine sslEngine = sslCtx.createSSLEngine();
                    sslEngine.setUseClientMode(true); //服务器端模式
                    ch.pipeline().addLast("ssl", new SslHandler(sslEngine));

                    ch.pipeline().addLast(new NetEncoder(),new NetDecoder(),new NetClientHandler(NetClient.this));
                }
            });


        }catch(Exception e){
            logger.error(ExceptionUtil.getMessage(e));
        }
    }

    //  连接到服务端
    void connect() {
        try {
            if(serverMsgListener!=null){
                serverMsgListener.onConnecting();
            }
            // Start the client.
            f = b.connect(new InetSocketAddress(serverIP, serverPort)); // (5)
            f.addListener(channelFutureListener);
        } catch (Exception e) {
            logger.error(ExceptionUtil.getMessage(e));
            routing(new NetMessage(NetMessageType.MESSAGE_CANNOT_RECONNECT,""));
        }
    }

    /**
     * 客户端退出
     */
    private void disConnectNet(){
        if(serverMsgListener!=null){
            serverMsgListener.onDisConnected();
        }
        workerGroup.shutdownGracefully();
    }

    private void sendMsg(NetMessage msg){
        try{
            if(f!=null&&f.isSuccess()){
                logger.debug("发送消息："+ msg.toString());
                f.channel().writeAndFlush(msg);
            }
        }catch (Exception e){
            logger.error(ExceptionUtil.getMessage(e));
        }
    }

    private Runnable netClientRunnable = new Runnable() {
        @Override
        public void run() {
            try {
                init();
                connect();
            } catch (Exception e) {
                logger.error(ExceptionUtil.getMessage(e));
            }
        }
    };

    void routing(NetMessage msg){
        try{
            switch (msg.getType()){
                /**
                 * 系统消息
                 */
                case MESSAGE_WELCOME:{ //欢迎信息
                    if(serverMsgListener!=null){
                        serverMsgListener.onConnected(new ServerMsgEvent(msg.getContent()));
                    }
                    break;
                }
                case MESSAGE_SERVER_ERROR:{
                    logger.debug("服务器错误,请重试");
                    disConnectNet();
                    break;
                }
                case MESSAGE_HEARTBEAT:{
                    //收到该消息表示服务端需要验证自己是否在线,返回一条心跳消息保持连接
                    sendMsg(new NetMessage(NetMessageType.MESSAGE_HEARTBEAT,""));
                    break;
                }
                case MESSAGE_CANNOT_RECONNECT:{
                    //收到该消息表示客服端已无法重连,需要重新初始化链接
                    logger.debug("连接服务器失败,请重新登录");
                    disConnectNet();
                    break;
                }
                case MESSAGE_KICK_OUT:{
                    //收到该消息表示客服端被强制下线
                    disConnectNet();
                    break;
                }
                case MESSAGE_USER_INPUT:{  //客户端主动发送的消息
                    if(serverMsgListener!=null){
                        serverMsgListener.onReciveMsg(new ServerMsgEvent(msg.getContent()));
                    }
                    break;
                }
            }
        }catch(Exception e){
            logger.error(ExceptionUtil.getMessage(e));
        }
    }

    //注册一个监听器
    private void register(ServerMsgListener serverMsgListener){
        this.serverMsgListener = serverMsgListener;
    }

    public String getServerName() {
        return serverName;
    }

    public String getServerIP() {
        return serverIP;
    }

    public int getServerPort() {
        return serverPort;
    }
}
